import {
    BufferGeometry, Vector3, Matrix4, BufferAttribute, InterleavedBufferAttribute
    , Euler,
} from "three";

const pi2 = Math.PI * 2;

export default class WaveBall extends BufferGeometry {
    widthSegments: number;
    heightSegments: number;
    r: number;
    //正弦函数 y = Asin(ωx+φ)
    a: number = 0.7;
    omega: number = 12;
    constructor(r: number = 1, widthSegments: number = 8, heightSegments: number = 8) {
        super();
        this.r = r;
        this.widthSegments = widthSegments;
        this.heightSegments = heightSegments;
        this._init();
    }

    _init() {
        const { widthSegments, heightSegments } = this;
        const [width, height] = [widthSegments + 1, heightSegments + 2];
        const perW = 360 / widthSegments;
        const perH = 180 / (heightSegments + 1);

        const normals: number[] = [];
        const elements: number[] = [];
        const vertices: number[] = [];

        const jd = -90 * Math.PI / 180;

        for (let i = 0; i < height; i++) {
            let phi = (90 - perH * i) * Math.PI / 180;
            for (let j = 0; j < width; j++) {
                let theta = (perW * j + 180) * Math.PI / 180;
                const posi = this._getPosition(theta, phi);
                const posi1 = posi.applyMatrix4(new Matrix4().makeRotationY(jd));

                vertices.push(posi1.x, posi1.y, posi1.z);
                const normalize = posi1.normalize();
                normals.push(normalize.x, normalize.y, normalize.z);

                if (i > 0 && j > 0) {
                    const lt = (i - 1) * width + (j - 1);
                    const rt = (i - 1) * width + j;
                    const lb = (i * width) + (j - 1);
                    const rb = (i * width) + j
                    elements.push(lt, lb, rb, lt, rb, rt);
                }
            }
        }

        this.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3));
        this.setAttribute("normal", new BufferAttribute(new Float32Array(normals), 3));
        this.setIndex(elements);
        //this.computeVertexNormals();
    }

    _getPosition(longitude: number, latitude: number) {
        const { r } = this;
        //欧拉角
        // const euler = new Euler(latitude, longitude, 0, "YXZ")
        // const cur = new Vector3(0, 0, 1).applyEuler(euler);
        // return cur;

        return new Vector3(
            r * Math.cos(latitude) * Math.cos(longitude),
            r * Math.sin(latitude),
            r * Math.cos(latitude) * -Math.sin(longitude),
        );
    }

    wave(phi: number = 0) {
        const { widthSegments, heightSegments, a, omega, r } = this;
        const [width, height] = [widthSegments + 1, heightSegments + 2];

        // 顶点点位
        const posAttr = this.getAttribute("position");
        // 一圈波浪线的总弧度
        const allAng = omega * pi2
        // 每个分段的弧度
        const yAng = allAng / heightSegments
        const xAng = allAng / widthSegments
        let index = 0;
        // 逐行列遍历
        for (let y = 0; y < height; ++y) {
            // y向起伏
            const r0 = Math.sin(y * yAng + phi)
            // 基于y值做起伏衰减
            const decay = a * 0.2 + a * (0.5 - Math.abs(y / heightSegments - 0.5))
            for (let x = 0; x < width; ++x) {
                // x向起伏
                const r1 = Math.sin(x * xAng + phi)
                // 基于半径修改顶点位置
                this._changePos(posAttr, index++, (r0 * r1) * decay * 0.2 + r);
                // changePos(r0 * r1 * decay + 1)
                // changePos(r1 * 0.1 + 1)
            }
        }
        //this.computeVertexNormals()
        posAttr.needsUpdate = true
    }

    _changePos(attr: BufferAttribute | InterleavedBufferAttribute, index: number, r: number) {
        const cur = new Vector3(attr.getX(index), attr.getY(index), attr.getZ(index));
        cur.setLength(r);
        attr.setXYZ(index, cur.x, cur.y, cur.z);
    }
}